using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Specialized;

public delegate void OnProgressHandler(long BytesRead);

/// <summary>
/// Struct for holding service specifics necessary for upload
/// </summary>
public struct HostingService 
{
	/// <summary>
	/// Description
	/// </summary>
	//public string Description;
	/// <summary>
	/// Complete URL where upload form is located
	/// </summary>
	public string Address;
	/// <summary>
	/// Name of input which accepts files
	/// </summary>
	public string FileFormName;
	/// <summary>
	/// Regular expression for matching output link for a file
	/// </summary>
	public Regex LinkPattern;
	
	public HostingService(string URL, string FileForm, Regex LinkRE)
	{
		Address = URL;
		FileFormName = FileForm;
		LinkPattern = LinkRE;
	}
}

/// <summary>
/// Instance of this class allows you to POST files through HTTP using custom hosting services
/// </summary>
public class FileHosting
{	
	private string address, fileFormName, htmlResponse;
	private Regex linkPattern;
	
	public event OnProgressHandler OnProgress;
	
	/// <summary>
	/// Creates an instance of class for uploading
	/// </summary>
	/// <param name="Hosting">Struct which contains parameters to a specific web hosting service</param>
	public FileHosting(HostingService Hosting)
	{
		address = Hosting.Address;
		fileFormName = Hosting.FileFormName;
		linkPattern = Hosting.LinkPattern;
	}
	
	/// <summary>
	/// Retrieve link to file using last response from UploadFile method
	/// </summary>
	/// <returns>String with URL to file</returns>
	public string GetLink()
	{
		Match link = linkPattern.Match(htmlResponse);
		return link.Groups[1].Value;
	}
	
	/// <summary>
	/// Uploads file through HTTP using POST
	/// </summary>
	/// <param name="Filename">Path to file to be uploaded</param>
	/// <param name="Data">Additional POST variables</param>
	/// <returns>HTML response as string</returns>
	public string UploadFile(string Filename, NameValueCollection Data)
	{		
		string uriData = string.Empty;
			
		if (Data != null)
		{
			foreach (string key in Data.Keys)
			{
				uriData += key + "=" + Data.Get(key) + "&";
			}
		}
			
		Uri uri = new Uri(address + uriData);
	
		string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
    	HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(uri);
    	webrequest.ContentType = "multipart/form-data; boundary=" + boundary;
    	webrequest.Method = "POST";

    	StringBuilder sb = new StringBuilder();
    	sb.Append("--");
 		sb.Append(boundary);
		sb.Append("\r\n");
		sb.Append("Content-Disposition: form-data; name=\"");
		sb.Append(fileFormName);
 		sb.Append("\"; filename=\"");
 		sb.Append(Path.GetFileName(Filename));
 		sb.Append("\"");
  		sb.Append("\r\n");
    	sb.Append("Content-Type: ");
    	sb.Append("image/jpeg");
   		sb.Append("\r\n");
    	sb.Append("\r\n");            

    	string postHeader = sb.ToString();
    	byte[] postHeaderBytes = Encoding.UTF8.GetBytes(postHeader);

    	// Build the trailing boundary string as a byte array
    	// ensuring the boundary appears on a line by itself

    	byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

    	FileStream fileStream = new FileStream(Filename, 
                               FileMode.Open, FileAccess.Read);
		long length = postHeaderBytes.Length + fileStream.Length + 
                                      boundaryBytes.Length;
		webrequest.ContentLength = length;

		Stream requestStream = webrequest.GetRequestStream();

		// Write out our post header

		requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

    	// Write out the file contents

    	byte[] buffer = new Byte[checked((uint)Math.Min(4096, 
    	                         (int)fileStream.Length))];
    	
    	long bytesRead = 0, totalBytesRead = 0;
    	while ( (bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0 )
    	{
    		requestStream.Write(buffer, 0, (int)bytesRead);
    		if (OnProgress != null)
    		{
    			totalBytesRead += bytesRead;
    			OnProgress.BeginInvoke(totalBytesRead, null, null);
    		}
    	}

    	// Write out the trailing boundary

    	requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
    	WebResponse responce = webrequest.GetResponse();
   	 	Stream s = responce.GetResponseStream();
    	StreamReader sr = new StreamReader(s);
    	
    	return (htmlResponse=sr.ReadToEnd());
	}
}
